
package com.joysuch.wwyt.datasync.service;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.Query;

import cn.hutool.core.lang.UUID;
import com.joysuch.wwyt.datasync.DataSyncType;
import com.joysuch.wwyt.datasync.annotation.DataSync;
import com.joysuch.wwyt.datasync.repository.DataSyncInfoDao;
import com.sun.star.lang.IllegalArgumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 * 1. nativeQuery=true, 使用sql查询，否则使用 hql查询<br>
 * 2. selectSql 必须是查询当前dao所对应的domain对象<br>
 * 3. selectSql 参数列表，必须和方法调用的参数数量、位置、类型一致<br>
 * 4. 对象是prototype方式创建对象，每次都重新创建对象
 * 
 * @date 2021年1月16日 下午2:14:28
 * @since
 *
 */
@Component
@Scope("prototype")
public class QueryObjectsBySqlDataSyncProcessor implements DataSyncProcessor {

    private static final Logger logger = LoggerFactory.getLogger(QueryObjectsBySqlDataSyncProcessor.class);

    List objects;

    @Resource
    private EntityManager entityManager;

    @Resource
    private DataSyncInfoDao infoDao;

    String processId;

    @Override
    public void processBeforeInvoking(Class domainClass, DataSync annotation, Method method, DataSyncType type,
            Object[] arguments) {
        processId = UUID.randomUUID().toString();
        try {
            logger.info("数据同步: 开始处理。processId={}, entity={}, hql={},args={}", processId, domainClass.getSimpleName(),
                    annotation.selectSql(), Arrays.toString(arguments));
            if (annotation.nativeQuery()) {
                Query query = entityManager.createNativeQuery(annotation.selectSql(), domainClass);
                // 处理参数
                arguments = processArguments(annotation, arguments);
                executeQuery(arguments, query);
            } else {
                Query query = entityManager.createQuery(annotation.selectSql());
                // 处理参数
                arguments = processArguments(annotation, arguments);
                executeQuery(arguments, query);
            }
            logger.info("数据同步: processId={},查询出{}条数据", processId, objects.size());
        } catch (Exception e) {
            logger.error("数据同步: processId={},失败", processId, e);
        }
    }

    /**
     * 处理配置了DataSync.argsIndex()参数索引的方法参数，将参数重新排列后，写入新的数组
     * 
     * @param annotation
     *            数据同步注解
     * @param arguments
     *            方法参数
     * @return
     * @throws IllegalArgumentException
     *             annotation.argsIndex().length> arguments.length时抛出
     */
    private Object[] processArguments(DataSync annotation, Object[] arguments) throws IllegalArgumentException {
        if (arguments == null) {
            return arguments;
        }
        if (annotation.argsIndex().length == 1 && annotation.argsIndex()[0] == -1) {
            // 默认不改变
            return arguments;
        } else if (annotation.argsIndex().length > arguments.length) {
            throw new IllegalArgumentException(
                    String.format("argsIndex()=%s, 大于参数个数%s ", annotation.argsIndex().length, arguments.length));
        } else {
            Object[] newArgs = new Object[annotation.argsIndex().length];
            for (int i = 0; i < annotation.argsIndex().length; i++) {
                newArgs[i] = arguments[annotation.argsIndex()[i]];
            }
            return newArgs;
        }
    }

    private void executeQuery(Object[] arguments, Query query) {
        if (arguments != null) {
            for (int i = 0; i < arguments.length; i++) {
                query.setParameter(i + 1, arguments[i]);
            }
        }
        objects = query.getResultList();
    }

    @Override
    public List processAfterInvoking(Class domainClass, DataSync annotation, Method method, DataSyncType type,
            Object[] arguments) {
        if (objects != null && objects.size() > 0) {
            return objects;
        } else {
            logger.warn("数据同步: processId={}, 没有数据要处理。", processId);
            return null;
        }
    }

}
